package com.itbac.util;

import cn.hutool.core.util.RandomUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: BacHe
 * @email: 1218585258@qq.com
 * @Date: 2021/12/19 0:11
 */
public class PayUtil {
    private static PrivateKey merchantPrivateKey;
    //这个是动态的:不能写死
    private static ScheduledUpdateCertificatesVerifier verifier;
    private static CloseableHttpClient httpClient;

    static {
        //这个是静态的
        merchantPrivateKey = PemUtil.loadPrivateKey(PayConstants.privateKey);
        // 使用定时更新的签名验证器，不需要传入证书
        refresh();
    }

    /**
     * 刷新证书 和  http客户端
     * 这个2个是动态的
     */
    private static void refresh() {
        verifier = new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(PayConstants.mchId,
                        new PrivateKeySigner(PayConstants.mchSerialNo, merchantPrivateKey)),
                PayConstants.apiV3Key.getBytes(StandardCharsets.UTF_8));
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(PayConstants.mchId, PayConstants.mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier))
                .build();
    }

    /**
     * 创建订单
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
     * @param openId 付款用户
     * @param outTradeNo 商户订单号
     * @param amount 支付金额 单位:分
     * @param desc 商品描述
     */
    public static Map<String, String> createOrder(String openId,String outTradeNo, Long amount, String desc) throws Exception {
        refresh();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", PayConstants.mchId)
                .put("appid", PayConstants.APP_ID)
                .put("notify_url", PayConstants.NOTIFY_URL)
                .put("description", desc)
                .put("out_trade_no",outTradeNo);
        rootNode.putObject("amount")
                .put("total", amount);
        rootNode.putObject("payer")
                .put("openid", openId);

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));

        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString);

    /*
        下单返回值
        {
            "prepay_id": "wx26112221580621e9b071c00d9e093b0000"
        }
    */
        //时间戳,单位:秒
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        //随机字符串
        String nonce = RandomUtil.randomString(32);

        StringBuilder builder = new StringBuilder();

        //应用ID
        builder.append(PayConstants.APP_ID).append("\n");
        //时间戳,单位:秒
        builder.append(timestamp).append("\n");
        //随机字符串
        builder.append(nonce).append("\n");
        JsonNode jsonNode = objectMapper.readTree(bodyAsString);
        String packageStr = "prepay_id=" + jsonNode.get("prepay_id");
        //预支付交易回话ID
        builder.append(packageStr).append("\n");

        //生成签名 (旧的)
//        String ciphertext = RsaCryptoUtil.encryptOAEP(builder.toString(), verifier.getLatestCertificate());
        //新的 ,生成签名
        String ciphertext = sign(builder.toString().getBytes("UTF-8"));

        Map<String, String> map = new HashMap<>();

        map.put("appId", PayConstants.APP_ID);
        map.put("timeStamp", timestamp);
        map.put("nonceStr", nonce);
        map.put("package", packageStr);
        map.put("signType", "RSA");
        map.put("paySign", ciphertext);

        //JSAPI调起支付API ,前端需要这些参数调起支付
        //https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
        return map;

    }

    /**
     * 新的签名方法
     */
    public static String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(merchantPrivateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 验证签名,微信支付回调时
     * @param serial    请求头里的证书序列号
     * @param message   组装生成签名的参数
     * @param signature 签名内容
     */
    public static boolean signVerify(String serial, String message, String signature) {
        try {
            refresh();
            return verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 解密,微信支付回调的内容
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
     */
    public static String decryptOrder(String body) {
        AesUtil aesUtil = new AesUtil(PayConstants.apiV3Key.getBytes(StandardCharsets.UTF_8));
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode jsonNode = objectMapper.readTree(body);
            JsonNode resource = jsonNode.get("resource");
            //微信支付返回的 密文
            String ciphertext = resource.get("ciphertext").textValue();
            String associatedData = resource.get("associated_data").textValue();
            String nonce = resource.get("nonce").textValue();
            return aesUtil.decryptToString(
                    associatedData.getBytes("UTF-8"),
                    nonce.getBytes("UTF-8"),
                    ciphertext
            );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭订单, 关闭客户未支付的订单
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml
     *
     * @param outTradeNo 客户的订单号
     */
    public static Boolean closeOrder(String outTradeNo) throws IOException {
        refresh();
        //本系统的订单号,拼在地址上了
        HttpPost httpPost = new HttpPost(
                String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close", outTradeNo));
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", PayConstants.mchId);

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        if (response.getStatusLine().getStatusCode() == 204) {
            System.out.println("微信回复,关单成功");
            return true;
        }
        System.out.println("微信回复,关单失败");
        return false;
    }

    /**
     * 查询订单支付状态
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml
     * @param outTradeNo 商户订单号
     */
    public static String getOrder(String outTradeNo) throws Exception {
        refresh();
        URIBuilder uriBuilder = new URIBuilder(
                String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s?mchid=%s", outTradeNo, PayConstants.mchId));
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader("Accept", "application/json");

        CloseableHttpResponse response = httpClient.execute(httpGet);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString);
        return bodyAsString;
    }
}
